#ifndef KBD_H
#define KBD_H

#include <stdint.h>
#include <stdbool.h>

// ibm keyboard scancode set 1
// https://wiki.osdev.org/PS/2_Keyboard#Scan_Code_Set_1
enum kbd_key {
    KBD_VK_ESC = 0x01,
    KBD_VK_1 = 0x02,
    KBD_VK_2 = 0x03,
    KBD_VK_3 = 0x04,
    KBD_VK_4 = 0x05,
    KBD_VK_5 = 0x06,
    KBD_VK_6 = 0x07,
    KBD_VK_7 = 0x08,
    KBD_VK_8 = 0x09,
    KBD_VK_9 = 0x0A,
    KBD_VK_0 = 0x0B,
    KBD_VK_MINUS = 0x0C,
    KBD_VK_EQUALS = 0x0D,
    KBD_VK_BACKSPACE = 0x0E,
    KBD_VK_TAB = 0x0F,
    KBD_VK_Q = 0x10,
    KBD_VK_W = 0x11,
    KBD_VK_E = 0x12,
    KBD_VK_R = 0x13,
    KBD_VK_T = 0x14,
    KBD_VK_Y = 0x15,
    KBD_VK_U = 0x16,
    KBD_VK_I = 0x17,
    KBD_VK_O = 0x18,
    KBD_VK_P = 0x19,
    KBD_VK_LBRACKET = 0x1A,
    KBD_VK_RBRACKET = 0x1B,
    KBD_VK_ENTER = 0x1C,
    KBD_VK_LCTRL = 0x1D,
    KBD_VK_A = 0x1E,
    KBD_VK_S = 0x1F,
    KBD_VK_D = 0x20,
    KBD_VK_F = 0x21,
    KBD_VK_G = 0x22,
    KBD_VK_H = 0x23,
    KBD_VK_J = 0x24,
    KBD_VK_K = 0x25,
    KBD_VK_L = 0x26,
    KBD_VK_SEMICOLON = 0x27,
    KBD_VK_QUOTE = 0x28,
    KBD_VK_BACKQUOTE = 0x29,
    KBD_VK_LSHIFT = 0x2A,
    KBD_VK_BACKSLASH = 0x2B,
    KBD_VK_Z = 0x2C,
    KBD_VK_X = 0x2D,
    KBD_VK_C = 0x2E,
    KBD_VK_V = 0x2F,
    KBD_VK_B = 0x30,
    KBD_VK_N = 0x31,
    KBD_VK_M = 0x32,
    KBD_VK_COMMA = 0x33,
    KBD_VK_PERIOD = 0x34,
    KBD_VK_SLASH = 0x35,
    KBD_VK_RSHIFT = 0x36,
    KBD_VK_KP_ASTERISK = 0x37,
    KBD_VK_LALT = 0x38,
    KBD_VK_SPACE = 0x39,
    KBD_VK_CAPSLOCK = 0x3A,
    KBD_VK_F1 = 0x3B,
    KBD_VK_F2 = 0x3C,
    KBD_VK_F3 = 0x3D,
    KBD_VK_F4 = 0x3E,
    KBD_VK_F5 = 0x3F,
    KBD_VK_F6 = 0x40,
    KBD_VK_F7 = 0x41,
    KBD_VK_F8 = 0x42,
    KBD_VK_F9 = 0x43,
    KBD_VK_F10 = 0x44,
    KBD_VK_NUMLOCK = 0x45,
    KBD_VK_SCROLLLOCK = 0x46,
    KBD_VK_KP_7 = 0x47,
    KBD_VK_KP_8 = 0x48,
    KBD_VK_KP_9 = 0x49,
    KBD_VK_KP_MINUS = 0x4A,
    KBD_VK_KP_4 = 0x4B,
    KBD_VK_KP_5 = 0x4C,
    KBD_VK_KP_6 = 0x4D,
    KBD_VK_KP_PLUS = 0x4E,
    KBD_VK_KP_1 = 0x4F,
    KBD_VK_KP_2 = 0x50,
    KBD_VK_KP_3 = 0x51,
    KBD_VK_KP_0 = 0x52,
    KBD_VK_KP_PERIOD = 0x53,
    KBD_VK_F11 = 0x57,
    KBD_VK_F12 = 0x58,
    KBD_VK_MASK = 0x80
};

enum kbd_status_bits {
    KBD_OBF      = 0x01, // 1 means output buffer full
    KBD_IBF      = 0x02, // 1 means input buffer full
    KBD_SYS      = 0x04, // 0 after power on
    KBD_CMD_DATA = 0x08, // 1 means command write, 0 means data write
    KBD_KEYLOCK  = 0x10, // 1 means keyboard is not inhibited
    KBD_AUX_OBF  = 0x20, // 1 means mouse output buffer full
    KBD_TIMEOUT  = 0x40, // 1 means timeout error
    KBD_PARITY   = 0x80  // 1 means parity error
};

enum kbd_indicator {
    KBD_INDICATOR_SCROLLLOCK = 0x01,
    KBD_INDICATOR_NUMLOCK    = 0x02,
    KBD_INDICATOR_CAPSLOCK   = 0x04
};

struct kbd_device {
    uint8_t (*status)(void);
    uint8_t (*poll_key)(void);
    uint8_t (*wait_key)(void);
    char (*to_ascii)(enum kbd_key key);
    uint8_t (*indicator)(void);
    void (*set_indicator_active)(enum kbd_indicator indicator, bool active);
};

void kbd_init(struct kbd_device* dev);

#endif // KBD_H